{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 问题：Python 支持函数式编程吗？为什么？从哪些方面可以看出来？\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "面试官想从这道题中考察面试人什么？\n",
    "近些年来，由于函数式编程易于调试和测试的优点，函数式编程越来越受到关注。虽然 Python 不是纯函数式编程，但它也提供了很多内置的函数和模块支持函数式编程。这道题中，我们不光要知道 Python 有哪些地方支持函数式编程，还必须知道函数式编程的基本思想，即不变性。\n",
    "\n",
    "答案\n",
    "Python 支持函数式编程，主要分以下几种：\n",
    "\n",
    "Python 的一些语言特性，比如lambda匿名函数、列表推导、字典推导、生成器、迭代器等\n",
    "Python 的一些内置函数，包括map、reduce、filter、all、any、enumerate、zip等高阶函数\n",
    "Python 的一些内置模块，比如 itertools、functools 和 operator模块等\n",
    "拓展\n",
    "近年来，函数式编程可以说是越来越热了，那么函数式编程到底是什么呢，下面我们一起来看一下：\n",
    "\n",
    "简单来说，函数式编程其实就是一个编程范式，也就是如何编写程序的一种方式。\n",
    "\n",
    "Tips: 函数式编程是面向数学的抽象，将计算描述为一种表达式求值。函数式程序就是一个表达式。\n",
    "\n",
    "函数式编程一大特征就是：函数是一等对象。也就是说，你的所有操作都可以由函数进行完成，函数和其他数据类型拥有平等的地位，可以赋值给变量，也可以作为参数传入另一个函数，或者作为别的函数的返回值。比如高阶函数，它们接受函数作为输入。\n",
    "\n",
    "下面，我举个例子，我同时用指令式编程和函数式编程两种方式去实现列表元素加1的功能，你们可以先自行体会一下两者编程思维的区别。\n",
    "\n",
    "# 指令式编程\n",
    "# 先确立初始状态，然后每次迭代都执行循环体中的一系列命令\n",
    "newList = []\n",
    "for num in range(1, 10):\n",
    "    newList.append(num + 1) #\n",
    "\n",
    "# 函数式编程\n",
    "# 以函数的方式，去对数据进行转换，我们可以这样想象这样一个函数f(x) = x + 1\n",
    "newList = list(map(lambda x: x + 1, list(range(1, 10))))\n",
    "在很多存函数式编程语言中，都会提供map，reduce和filter这三个高阶函数。它们实现了低层次的变化，仅依赖于开发者自定义的高阶函数作为参数来调整低层次的运转机制。\n",
    "\n",
    "Tips: 高阶函数是指可以将其他函数作为参数或者返回结果的函数。比如，映射函数map会对输入集合的每个元素执行参数内提供的函数代码块，并返回变换后的集合。传给映射函数的是一个高阶函数和一个集合，它在对集合中的每一个元素施用传入的函数之后，产生另一个集合作为返回值。\n",
    "\n",
    "另外，闭包也是一等函数的一个体现。闭包实际上是指在暗地里绑定了函数内部引用的所有变量。换句话说，这种函数把它引用的所有东西都放在一个上下文里“包”了起来。关于闭包更具体的讲解会在下一题中详细展开。\n",
    "\n",
    "使用递归来解决迭代问题也是典型的函数式编程。因为函数式编程不保存状态，那么就使用递归，函数重复地以子函数的形式调用自己，间接地将每次输出的状态值当做下一次的输入参数。它的好处就是可以简化问题。但是，我们之前在做斐波那契数列时候就发现使用递归的一个缺点：其运行速度会变得越来越慢，性能较差。\n",
    "\n",
    "关于函数式编程，不是三两句就能说完的，可能得花上很大的功夫才能讲明白。最后，再讲一个函数式风格的例子，就是使用functools.partial()函数。对于用函数式风格编写的程序，有时你会希望通过给定部分参数，将已有的函数构变形称新的函数。考虑一个 Python 函数f(a, b, c)；你希望创建一个和 f(1, b, c) 等价的新函数g(b, c)；也就是说你给定了f()的一个参数的值。这就是所谓的“部分函数应用”。而functools.partial()这个函数的主要功能就是，填充一些参数去将现有的函数变体构建出新的函数形式，由此也增强了代码的可读性。\n",
    "\n",
    "from functools import partial\n",
    "\n",
    "def log_template(level, message):\n",
    "    print(\"{}: {}\".format(level, message))\n",
    "\n",
    "log_info = partial(log_template, \"info\")\n",
    "log_warning = partial(log_template, \"warning\")\n",
    "log_error = partial(log_template, \"error\")\n",
    "上面这段代码，把一个函数变成了三个:log_info，log_warning和log_error。它们的职责明确，代码清晰可读，这就是函数式编程的一大优点。另外，函数式编程中的函数大多都很小，这让代码更易调试，并且它也不依赖于系统状态，这就让测试变得简单。这些优点也是函数式编程越来越受青睐的主要原因。\n",
    "\n",
    "问题：什么是lambda函数？它有什么好处?\n",
    "面试官想从这道题中考察面试人什么？\n",
    "要想知道面试官考察什么，就要先知道 lambda 是什么。lambda 是 Python 的一个语法糖，我们一般使用它来创建一些简答的函数，更常用在 map 和 reduce 函数中进行搭配使用。所以，在面试时，考官可能会要求你结合这两个函数去实现一些简单的功能。\n",
    "\n",
    "答案\n",
    "lambda表达式被用于创建匿名函数，一般在一些一次性运行的程序中使用。因为不需要定义函数名，所以不用担心函数名称冲突的问题，而且匿名函数更加的节省内存中的变量定义空间。\n",
    "\n",
    "拓展\n",
    "lambda函数很容易理解。你们可以参考之前讲解的第 3 小节的高阶函数去查看map，filter的使用方法，里面有很多代码段都用到了lambda函数。\n",
    "\n",
    "另外，要注意在lambda函数定义的变量是自由变量。这是什么意思呢？我们可以先看下面这个例子：\n",
    "\n",
    "Tips: 自由变量是指未在本地作用域中绑定的变量。\n",
    "\n",
    ">>> x = 10\n",
    ">>> func1 = lambda y: x + y\n",
    ">>> x = 20\n",
    ">>> func2 = lambda y: x + y\n",
    ">>> func1(10)\n",
    "30\n",
    ">>> func2(10)\n",
    "30\n",
    "为什么 func1(10)和func2(10)的输出都是 30 呢？这是因为x是一个自由变量，只有在运行的时候绑定到函数中，而非定义时绑定。\n",
    "\n",
    "如果我想在定义的时候去绑定变量，应该怎么做呢？可以使用函数默认值参数形式。\n",
    "\n",
    ">>> x = 10\n",
    ">>> func3 = lambda y, x=x: x + y # x=x这样可以在定义时绑定到值\n",
    ">>> x = 20\n",
    ">>> func3(10) # 输出为20，而不是30\n",
    "你再想想有个列表变量是用匿名函数和列表推导定义的，像这样l = [lambda x: x + n for n in range(3)]。和上面提到一样，每次迭代，n 其实是一个自由变量，也就是说每次迭代对匿名函数求值都是用最后一次迭代的变量，即n=2.\n",
    "\n",
    ">>> for i in l:\n",
    "...print(i(0))\n",
    "...\n",
    "2\n",
    "2\n",
    "2\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}